package studio.xiaoyun.common.tool;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.Optional;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLTextExtractor;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import info.monitorenter.cpdetector.io.ASCIIDetector;
import info.monitorenter.cpdetector.io.CodepageDetectorProxy;
import info.monitorenter.cpdetector.io.JChardetFacade;
import info.monitorenter.cpdetector.io.ParsingDetector;
import info.monitorenter.cpdetector.io.UnicodeDetector;
import info.monitorenter.cpdetector.io.UnknownCharset;
import studio.xiaoyun.core.exception.CoreException;

public class FileTool {
    private static Logger log = LoggerFactory.getLogger(FileTool.class);
    
    /**
     * 检测输入流的编码
     * @param input 输入流
     * @param length 检测的字符数，字符数越多，检测越准确
     * @return 输入流的编码，如果无法检测或者检测出错，则返回空的Optional实例
     */
    public static Optional<Charset> getCharset(InputStream input,int length){
    	CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
		detector.add(new ParsingDetector(false));
		detector.add(UnicodeDetector.getInstance());
		detector.add(JChardetFacade.getInstance());
		detector.add(ASCIIDetector.getInstance());
		Charset charset = null;
		try {
			charset = detector.detectCodepage(input,length);
		} catch (Exception e) {
			log.error("检测输入流编码失败", e);
		}
		if(charset instanceof UnknownCharset){
			charset = null;
		}
		return Optional.ofNullable(charset);
    }

    /**
     * 检测文件的编码
     * @param fileName 文件名
     * @return 文件的编码，如果无法检测或者检测出错，则返回空的Optional实例
     */
    public static Optional<Charset> getCharset(String fileName){
    	CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
		detector.add(new ParsingDetector(false));
		detector.add(UnicodeDetector.getInstance());
		detector.add(JChardetFacade.getInstance());
		detector.add(ASCIIDetector.getInstance());
		Charset charset = null;
		try {
			File file = new File(fileName);
			charset = detector.detectCodepage(file.toURI().toURL());
		} catch (Exception e) {
			log.error("检测文件编码失败", e);
		}
		if(charset instanceof UnknownCharset){
			charset = null;
		}
		return Optional.ofNullable(charset);
    }
    
    /**
     * 从文件名中截取文件扩展名
     * @param fileName 文件名
     * @return 文件扩展名的小写形式，如果文件没有扩展名，则返回长度为0的空字符串
     */
    public static String getFileExtension(String fileName){
        int index = fileName.lastIndexOf(".");
        String extension = null;
        if(index>0){
            extension = fileName.substring(index+1).toLowerCase();
        }else{
            extension = "";
        }
        return extension;
    }
    
    /**
     * 读取文件的文本内容，不带格式，支持doc,docx,txt等文件类型
     * @param fileName 文件名
     * @return 文件的文本内容
     * @throws CoreException 如果读取出错，则抛出异常
     */
    public static String readText(String fileName) throws CoreException{
    	String text = null;
    	String extension = getFileExtension(fileName);
    	switch(extension){
    	 case "doc":
    		text = readTextForDoc(fileName);
    		break;
    	 case "docx":
    		text = readTextForDocx(fileName);
    		break;
    	 default:
    		text = readTextFile(fileName);
    	}
    	return text;
    }
    
    /**
     * 读取纯文本文件的内容，可以自动判断文件编码
     * @param fileName 文件名
     * @return 文件内容
     */
    public static String readTextFile(String fileName){
    	String text = null;
    	Optional<Charset> charset = getCharset(fileName);
    	String extension = getFileExtension(fileName);
    	if(!charset.isPresent()){
    		text = readTextFile(fileName,Charset.forName("GBK"));
    	}
    	//在中文版windows中用记事本保存的txt文件，检测出的编码是windows-1252，但是实际上是GBK编码
    	else if(charset.get().name().equals("windows-1252") && extension.equals("txt")){
    		text = readTextFile(fileName,Charset.forName("GBK"));
    	}else{
    		text = readTextFile(fileName,charset.get());
    	}
    	return text;
    }
    
    /**
     * 根据指定编码读取纯文本文件的内容
     * @param fileName 文件名
     * @param charset 编码
     * @return 文件内容
     */
    public static String readTextFile(String fileName,Charset charset){
        InputStreamReader in = null;
        File file = new File(fileName);
        if(!file.exists()){
            throw new CoreException("文件不存在:"+fileName);
        }
        StringBuffer sb = new StringBuffer((int)file.length());
        try{
        	in = new InputStreamReader(new FileInputStream(file),charset);
            char[] b = new char[1024];
            int n = -1;
            while((n=in.read(b))!=-1){
                sb.append(b,0,n);
            }
        }catch(Exception e){
            log.error("写入文件时出现错误",e);
            throw new CoreException("写入文件时出现错误");
        }finally{
            try {
                if (in != null) {
                	in.close();
                }
            }catch(IOException e){
                log.error("关闭文件流时出现错误",e);
            }
        }
        return sb.toString();
    }
	
    /**
     * 读取doc文件的文本，不带格式
     * @param fileName 文件名
     * @return 文件的文本内容
     */
    public static String readTextForDoc(String fileName){
    	String text = null;
    	FileInputStream in = null;
    	WordExtractor wordExtractor = null;
    	try{
    		in = new FileInputStream(fileName);
			wordExtractor = new WordExtractor(in);
    		text = wordExtractor.getText();
    	}catch(Exception e){
    		log.error("读取文件出错:"+fileName, e);
    		throw new CoreException("读取文件出错:"+fileName);
    	}finally{
    		try{
    			wordExtractor.close();
    			in.close();
    		}catch(Exception e){
    			log.error("关闭流出错:"+fileName, e);
    		}
    	}
    	return text;
    }

    /**
	 * 读取docx文件的内容，只读取纯文本内容，不带格式
	 * @param fileName 文件名
	 * @return 文件内容
	 */
	public static String readTextForDocx(String fileName) {
		String text = null;
		POIXMLTextExtractor ex = null;
		try{
			OPCPackage oPCPackage = POIXMLDocument.openPackage(fileName);
			XWPFDocument xwpf = new XWPFDocument(oPCPackage);
			ex = new XWPFWordExtractor(xwpf);
			text = ex.getText();
		}catch(Exception e){
			log.error("读取文件出错:"+fileName, e);
    		throw new CoreException("读取文件出错:"+fileName);
		}finally{
			try{
				ex.close();
			}catch(Exception e){
				log.error("关闭流出错:"+fileName, e);
			}
		}
		return text;
	}

    /**
     * 根据指定编码写文件
     * @param fileName 文件名
     * @param fileContent 文件名
     * @param charset 编码
     */
    public static void writeFile(String fileName,String fileContent,Charset charset){
        OutputStreamWriter out = null;
        File file = new File(fileName);
        if(file.exists()){
            file.delete();
        }
        try {
        	out = new OutputStreamWriter(new FileOutputStream(file),charset);
        	out.write(fileContent);
        }catch (Exception e) {
            log.error("写文件时出错", e);
            throw new CoreException("写文件时出错!"+e.getMessage());
        }finally{
            try {
                if (out != null) {
                	out.close();
                }
            } catch (IOException e) {
                log.error("关闭文件流失败", e);
            }
        }
    }
    
}
